#include "odbc_defines.h"
#include "odbc_api_internal.h"
#include "utils.h"
/*
* Allocate a HENV, HDBC, or HSTMT handle.
* @param type handle type
* @param input input handle (HENV, HDBC)
* @param output pointer to output handle (HENV, HDBC, HSTMT)
* @result ODBC error code
*/
SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT type, SQLHANDLE input, SQLHANDLE *output)
{
	SQLRETURN ret;

	switch (type) {
		case SQL_HANDLE_ENV:
			ret = op_alloc_env((SQLHENV *) output);
			if (ret == SQL_SUCCESS) {
				env_t *e = (env_t *) *output;
				if (e && e->magic == ENV_MAGIC) {
					e->ov3 = 1;
				}
			}
			return ret;
		case SQL_HANDLE_DBC:
			return op_alloc_connect((SQLHENV) input, (SQLHDBC *) output);
		case SQL_HANDLE_STMT:
			HDBC_LOCK((SQLHDBC) input);
			ret = op_alloc_stmt((SQLHDBC) input, (SQLHSTMT *) output);
			HDBC_UNLOCK((SQLHDBC) input);
			return ret;
	}

	return SQL_ERROR;
}

/*
* Free a HENV, HDBC, or HSTMT handle.
* @param type handle type
* @param h handle (HENV, HDBC, or HSTMT)
* @result ODBC error code
*/
SQLRETURN SQL_API SQLFreeHandle(SQLSMALLINT type, SQLHANDLE h)
{
	switch (type) {
		case SQL_HANDLE_ENV:
			return op_free_env((SQLHENV) h);
		case SQL_HANDLE_DBC:
			return op_free_connect((SQLHDBC) h);
		case SQL_HANDLE_STMT:
			return op_free_stmt((SQLHSTMT) h, SQL_DROP);
	}
	return SQL_ERROR;
}

/*
Set information in HENV.
@param env environment handle
@param attr attribute to be retrieved
@param val parameter buffer
@param len length of parameter
@result ODBC error code
*/
SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV env,
								SQLINTEGER attr, 
								SQLPOINTER val,
								SQLINTEGER len)
{
	env_t *e;
	SQLRETURN ret = SQL_ERROR;

	if (env == SQL_NULL_HENV) {
		return SQL_INVALID_HANDLE;
	}

	e = (env_t *) env;

	if (!e || e->magic != ENV_MAGIC) {
		return SQL_INVALID_HANDLE;
	}

#if defined(_WIN32) || defined(_WIN64)
	EnterCriticalSection(&e->cs);
	e->owner = GetCurrentThreadId();
#endif

	switch(attr) {
	case SQL_ATTR_CONNECTION_POOLING:
		ret = SQL_SUCCESS;
		break;
	case SQL_ATTR_CP_MATCH:
		ret = SQL_NO_DATA;
		break;
	case SQL_ATTR_OUTPUT_NTS:
		if (val == (SQLPOINTER) SQL_TRUE) {
			ret = SQL_SUCCESS;
		}
		break;
	case SQL_ATTR_ODBC_VERSION:
		if (!val) {
			break;		
		}

		if (val == (SQLPOINTER) SQL_OV_ODBC2) {
			e->ov3 = 0;
			ret = SQL_SUCCESS;
		}

		if (val == (SQLPOINTER) SQL_OV_ODBC3) {
			e->ov3 = 1;
			ret = SQL_SUCCESS;
		}
		break;
	}

#if defined(_WIN32) || defined(_WIN64)
	e->owner = 0;
	LeaveCriticalSection(&e->cs);
#endif

	return ret;
}

SQLRETURN SQL_API SQLSetConnectAttr(SQLHDBC hdbc,
									SQLINTEGER attribute,
									SQLPOINTER value,
									SQLINTEGER value_len)
{
	SQLRETURN ret;

	HDBC_LOCK(hdbc);
	ret = op_set_connect_attr(hdbc, attribute, value, value_len);
	HDBC_UNLOCK(hdbc);
	return ret;
}

/* 
Prepare HSTMT.
@param stmt statement handle
@param query query string
@param queryLen length of query string or SQL_NTS
@result ODBC error code
*/
SQLRETURN SQL_API SQLPrepare(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen)
{
	SQLRETURN ret;

	HSTMT_LOCK(stmt);
	ret = op_prepare(stmt, query, queryLen);
	HSTMT_UNLOCK(stmt);

	return ret;
}


/*
Bind parameter on HSTMT.
@param stmt statement handle
@param pnum parameter number, starting at 1
@param iotype input/output type of parameter
@param buftype type of host variable
@param ptype
@param coldef
@param scale
@param data pointer to host variable
@param buflen length of host variable
@param len output length pointer
@result ODBC error code
*/
SQLRETURN SQL_API SQLBindParameter(SQLHSTMT stmt,
								   SQLUSMALLINT pnum,
								   SQLSMALLINT iotype,
								   SQLSMALLINT buftype, 
								   SQLSMALLINT ptype, 
								   SQLUINTEGER coldef,
								   SQLSMALLINT scale, 
								   SQLPOINTER data, 
								   SQLINTEGER buflen,
								   SQLLEN *len)  
{
	SQLRETURN ret;
	HSTMT_LOCK(stmt);
	ret = op_bind_param(stmt, pnum, iotype, buftype, ptype, coldef, scale, data, buflen, len);
	HSTMT_UNLOCK(stmt);

	return ret;
}

SQLRETURN SQL_API SQLGetStmtAttr(SQLHSTMT hstmt,
								 SQLINTEGER attribute,
								 SQLPOINTER value,								 
								 SQLINTEGER value_max, 
								 SQLINTEGER *value_len)
{
	SQLRETURN NEED_CODE = 0;
	return NEED_CODE;
}

SQLRETURN SQL_API SQLSetDescField(SQLHDESC hdesc,
								  SQLSMALLINT record, 
								  SQLSMALLINT field,
								  SQLPOINTER value, 
								  SQLINTEGER value_len)
{
	SQLRETURN NEED_CODE = 0;
	return NEED_CODE;
}

/*
Execute query.
@param stmt statement handle
@result ODBC error code
*/
SQLRETURN SQL_API SQLExecute(SQLHSTMT stmt)
{
	SQLRETURN ret;

	HSTMT_LOCK(stmt);
	ret = op_execute(stmt, 1);
	HSTMT_UNLOCK(stmt);
	return ret;
}
